package com.kyk.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kyk.util.JwtUtil;
import lombok.Data;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.MediaType;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.*;

@Component
public class UserAuthFilter extends AbstractGatewayFilterFactory<UserAuthFilter.Config> {

    public UserAuthFilter() {
        super(Config.class);
    }


    @Override
    public GatewayFilter apply(Config config) {
        return ((exchange, chain) -> {
            RequestPath path = exchange.getRequest().getPath();
            if (config.enabled == false){
                //未开启，直接放行
                return chain.filter(exchange);

            }else if ("/auth/login".equals(path.toString())){
                //登录接口，直接放行
                return login(exchange,chain);
            }else if ("/auth/logout".equals(path.toString())){
                //退出登录接口，直接放行
                return logout(exchange,chain);
            }else if ("/auth/myinfo".equals(path.toString())){
                //用户信息接口，直接放行
                return myinfo(exchange,chain);
            }else {
                return chain.filter(exchange);
            }
        });
    }

    @Override
    public String name() {
        return "UserAuthFilter";
    }

    private Set<String> blackJwtSet = Collections.synchronizedSet(new HashSet<>());

    private Mono<Void> login(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("-----------------------login");
        ServerHttpResponse response = exchange.getResponse();
        //实现后置拦截
        //读取响应体，响应体读取完成之后，需要重新写响应体
        //如果登录成功，{code：200，msg：登录成功，，data：{...用户信息...}}
        //  根据data生成jwt，并设置到响应头中
        //如果登录失败 ，{code：400，msg：登录失败}
        //直接返回
        ServerHttpResponseDecorator decorator =
                new ServerHttpResponseDecorator(exchange.getResponse()) {
           // @Override
//            public Mono<Void> writeWith(Flux<DataBuffer> body) {
//                System.out.println("=============1==================");
//                return super.writeWith(body);
//            }
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                Flux<DataBuffer> flux = Flux.from(body).map(dataBuffer -> {
                    //读取响应体，转为字符串
                    byte[] bytes = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(bytes);
                    String bodyStr = new String(bytes);
                    try {
                        HashMap res = new ObjectMapper().readValue(bodyStr, HashMap.class);
                        System.out.println("res = " + res);
                        if (res.containsKey("data")){
                            //登录成功，生成jwt，并设置到响应头
                            //创建jwt
                            String jwt = JwtUtil.createJWT(30, (Map<String, Object>) res.get("data"));
                            response.getHeaders().set("Authorization","Bearer "+jwt);
                        }
                        //讲原始数据写回给响应体
                        return response.bufferFactory().wrap(bytes);
                    }catch (JsonProcessingException e){
                        throw new RuntimeException(e);
                    }
                });
                return super.writeWith(flux);
            }
        };
        return chain.filter(exchange.mutate().response(decorator).build());
    }

    private Mono<Void> logout(ServerWebExchange exchange,GatewayFilterChain chain){
        // 实现前置拦截
        // 提取 JWT 存入黑名单
        String jwt = exchange.getRequest().getHeaders().getFirst("Authorization");
        if(jwt!=null){
            blackJwtSet.add(jwt);
        }
        Map result = Map.of("code",1,"msg","退出成功");
        try {
            String json = new ObjectMapper().writeValueAsString(result);
            exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
            // 创建返回结果
            DataBuffer body = exchange.getResponse().bufferFactory().wrap(json.getBytes());
            // 直接将结果返回到浏览器
            return exchange.getResponse().writeWith(Mono.just(body));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
    private Mono<Void> myinfo(ServerWebExchange exchange,GatewayFilterChain chain){

        // 前置拦截
        //从请求头获取jwt
        String jwt = exchange.getRequest().getHeaders().getFirst("Authorization");
        // 判断黑名单中是否包含该 jwt
        if(blackJwtSet.contains(jwt)){
            jwt = null;
        }
        Map myinfo = null;
        if (jwt != null && jwt.startsWith("Bearer ")){
            //校验jwt
            try {
                myinfo = JwtUtil.parseJWT(jwt.substring(7));
            }catch (Exception e){
                //格式错误 超时
                throw new RuntimeException("解析jwt令牌错误");
            }
        }
        Map result;
        if (myinfo == null){
            result = Map.of("code" ,0 ,"msg" ,"未登录");
        }else {
            result = Map.of("code" ,1 ,"msg" ,"已登录","data",myinfo);
        }
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        ObjectMapper mapper = new ObjectMapper();
        try {
            String json = mapper.writeValueAsString(result);
            //创建返回结果
            DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(json.getBytes());
            //直接将结果返回浏览器
            return exchange.getResponse().writeWith(Mono.just(buffer));
        }catch (JsonProcessingException e){
            throw new RuntimeException(e);
        }
    }


    public static class Config {
        // 配置参数
        boolean enabled = false;

        public boolean isEnabled() {
            return enabled;
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }

        Config(){

        }
    }

    /**
     * 存在的问题:
     * 1. jwtToken 复制给他人使用, 如何阻止
     * 2. 基于 js sessionStoreage, 访问网页(点击超链接,无法写入Authorization), 如何实现权限的控制
     */

}
